#include	<stdio.h>
#include	<math.h>
#include	<allegro.h>

#include	"error.h"
#include	"sprites.h"
//#include	"common.h"		// needed for screen resolution size
#include	"draw.h"		// needed for 'pal'

unsigned char GetAnimationSpeed(unsigned short s, unsigned char *a){
	int SheetSize;
	
	unsigned short NumberOfSprites;
	unsigned short NumberOfAnimations;
	signed short SpriteSheetSizeX;
	signed short SpriteSheetSizeY;
	
	NumberOfSprites = SpriteTable[SpriteAddressTable[s]];
	NumberOfAnimations = SpriteTable[SpriteAddressTable[s]+1];
	SpriteSheetSizeX = SpriteTable[SpriteAddressTable[s]+2];
	SpriteSheetSizeY = SpriteTable[SpriteAddressTable[s]+3];
	
	SheetSize = SpriteSheetSizeX * SpriteSheetSizeY;
	if(*a >= NumberOfAnimations)
		return 1;	// prevent engine from crashing
		
	return SpriteTable[SpriteAddressTable[s] + (NumberOfSprites << 2) + (SheetSize << 2) + 4 + (*a << 8)];
}

unsigned char ReadAnimation(unsigned short s, unsigned char *a, unsigned short *frame, char *ti, unsigned char *AnimationSpeed){
	int n;
	int SheetSize;
	
	unsigned short NumberOfSprites;
	unsigned short NumberOfAnimations;
	signed short SpriteSheetSizeX;
	signed short SpriteSheetSizeY;
	
	/*signed short SpriteSizeXL;
	signed short SpriteSizeXR;
	signed short SpriteSizeYT;
	signed short SpriteSizeYB;*/
	
	//unsigned char NumberOfFrames;
	//unsigned short AnimationSpeed;
	
	NumberOfSprites = SpriteTable[SpriteAddressTable[s]];
	NumberOfAnimations = SpriteTable[SpriteAddressTable[s]+1];
	SpriteSheetSizeX = SpriteTable[SpriteAddressTable[s]+2];
	SpriteSheetSizeY = SpriteTable[SpriteAddressTable[s]+3];
	
	SheetSize = SpriteSheetSizeX * SpriteSheetSizeY;
	if(*a >= NumberOfAnimations)
		return 1;	// prevent engine from crashing
	
	if(*AnimationSpeed == 0)
		*AnimationSpeed = 1;
	
	if(*ti < 0){
		*ti = *AnimationSpeed;
		*frame += 1;
	}
	
	// Read_Sprite
	n = SpriteTable[SpriteAddressTable[s] + (NumberOfSprites << 2) + (SheetSize << 2) + 4 + (*a << 8) + 1 + *frame];
	if(n == 0xFFFF)			// SPECIAL: Restart
		*frame = 0;
	else if(n == 0xFFFE)	// SPECIAL: Loop from position
		*frame -= SpriteTable[SpriteAddressTable[s] + (NumberOfSprites << 2) + (SheetSize << 2) + 4 + (*a << 8) + 2 + *frame];
	else if(n == 0xFFFD){	// SPECIAL: Start new animation
		*a = SpriteTable[SpriteAddressTable[s] + (NumberOfSprites << 2) + (SheetSize << 2) + 4 + (*a << 8) + 2 + *frame];
		*frame = 0;
		*AnimationSpeed = GetAnimationSpeed(s, a);
	}
	
	return n;
}

unsigned char DrawAnimation(ZONE *z, BITMAP *l, unsigned short s, unsigned char a, unsigned short frame, char m, unsigned char tr, char *ti, unsigned char AnimationSpeed, short x, short y){
	int n;
	int SheetSize;
	
	unsigned short NumberOfSprites;
	unsigned short NumberOfAnimations;
	signed short SpriteSheetSizeX;
	signed short SpriteSheetSizeY;
	
	NumberOfSprites = SpriteTable[SpriteAddressTable[s]];
	NumberOfAnimations = SpriteTable[SpriteAddressTable[s]+1];
	SpriteSheetSizeX = SpriteTable[SpriteAddressTable[s]+2];
	SpriteSheetSizeY = SpriteTable[SpriteAddressTable[s]+3];
	
	SheetSize = SpriteSheetSizeX * SpriteSheetSizeY;
	if(a >= NumberOfAnimations)
		return 1;	// prevent engine from crashing
	
	n = SpriteTable[SpriteAddressTable[s] + (NumberOfSprites << 2) + (SheetSize << 2) + 4 + (a << 8) + 1 + frame];
	
	if(draw_frame && n < NumberOfSprites){
		DrawSprite(z, l, s, n, m, tr, x, y);
		DrawSprite(z, l, s, n, m, tr, x, y - (z->Act[act].Stage[stage].WrapPoint));
		DrawSprite(z, l, s, n, m, tr, x, y + (z->Act[act].Stage[stage].WrapPoint));
	}
	
	//*ti += 1;	// Cannot use increment operator
	*ti -= 1;
	return 0;
}

void DrawSprite(ZONE *z, BITMAP *l, unsigned short s, unsigned char ss, char m, unsigned char tr, short x, short y){
	// s = sprite
	// ss = sub-sprite
	int a;
	int b;
	int c;
	int d;
	
	unsigned short val_pixel;
	
	// Index
	unsigned short color_i;
	// Sprite
	unsigned char color_rs;
	unsigned char color_gs;
	unsigned char color_bs;
	// Background
	unsigned char color_rb;
	unsigned char color_gb;
	unsigned char color_bb;
	
	char mirror;
	char flip;
	
	unsigned short NumberOfSprites;
	unsigned short NumberOfAnimations;
	signed short SpriteSheetSizeX;
	signed short SpriteSheetSizeY;
	
	signed short SpriteSizeXL;
	signed short SpriteSizeXR;
	signed short SpriteSizeYT;
	signed short SpriteSizeYB;
	
	//unsigned char NumberOfFrames;
	//unsigned char AnimationSpeed;
	
	if(!draw_frame)
		return;
	
	NumberOfSprites = SpriteTable[SpriteAddressTable[s]];
	NumberOfAnimations = SpriteTable[SpriteAddressTable[s]+1];
	SpriteSheetSizeX = SpriteTable[SpriteAddressTable[s]+2];
	SpriteSheetSizeY = SpriteTable[SpriteAddressTable[s]+3];
	
	SpriteSizeXL = SpriteTable[SpriteAddressTable[s] + (SpriteSheetSizeX * SpriteSheetSizeY << 2) + 4 + (ss << 2)];
	SpriteSizeXR = SpriteTable[SpriteAddressTable[s] + (SpriteSheetSizeX * SpriteSheetSizeY << 2) + 4 + (ss << 2)+1];
	SpriteSizeYT = SpriteTable[SpriteAddressTable[s] + (SpriteSheetSizeX * SpriteSheetSizeY << 2) + 4 + (ss << 2)+2];
	SpriteSizeYB = SpriteTable[SpriteAddressTable[s] + (SpriteSheetSizeX * SpriteSheetSizeY << 2) + 4 + (ss << 2)+3];
	
	mirror = (SpriteSizeXL >> 15) & 1;
	flip = (SpriteSizeYT >> 15) & 1;
	
	SpriteSizeXL &= 0x7FFF;
	SpriteSizeYT &= 0x7FFF;
	
	if(ss >= NumberOfSprites)
		return;		// prevent engine from crashing
	
	switch(m){
		case NORMAL:
			break;
		case MIRROR:
			mirror ^= 1;
			break;
		case FLIP:
			flip ^= 1;
			break;
		case ROTATE:
			mirror ^= 1;
			flip ^= 1;
	}
	
	if(screen_scale_x && !screen_double_x)
		x -= 16;
	
	
	
	//allegro_message("%i, %i", x, y);
	/*if(y + (SpriteSizeYB - SpriteSizeYT) >= screen_resolution_y){
		SpriteSizeYB -= (y + (SpriteSizeYB - SpriteSizeYT)) - screen_resolution_y - 1;
	}
	if(x + (SpriteSizeXR - SpriteSizeXL) >= screen_resolution_x){
		SpriteSizeXR -= (x + (SpriteSizeXR - SpriteSizeXL)) - screen_resolution_x - 1;
	}
	if(y < 0){
		abs(y);
		SpriteSizeYT += y;
	}
	if(x < 16){
		x = abs(x - 16);
		SpriteSizeXL += x;
	}
	//allegro_message("%i, %i", x, y);
	if(ticCounter60R == 0)
	allegro_message("%i, %i, %i, %i", SpriteSizeXL, SpriteSizeXR, SpriteSizeYT, SpriteSizeYB);
	
	
	
	if(SpriteSizeYT <= SpriteSizeYB && SpriteSizeXL <= SpriteSizeXR)
	{*/
	if(tr > 0){
		for(a = SpriteSizeYT; a <= SpriteSizeYB; a += (1<<(screen_scale_y >> screen_double_y))){
			for(b = SpriteSizeXL; b <= SpriteSizeXR; b += (1<<(screen_scale_x >> screen_double_x))){
				//allegro_message("%i, %i", x + (b - SpriteSizeXL), y + (a - SpriteSizeYT));
				if(y + (a - SpriteSizeYT) >= 0 && y + (a - SpriteSizeYT) < screen_buffer_y){
					if(x + (b - SpriteSizeXL) >= (16>>screen_scale_x) && x + (b - SpriteSizeXL) < screen_buffer_x-16){
						//if(SpriteTable[SpriteAddressTable[s] + 4 + (a*SpriteSheetSizeX << 2) + b+1] != 0xFF){
							if(flip == 0)
								c = a * SpriteSheetSizeX;
							else
								c = (SpriteSizeYB + (SpriteSizeYT - a)) * SpriteSheetSizeX;							
							
							
							if(mirror == 0)
								d = b;
							else
								d = SpriteSizeXR + (SpriteSizeXL - b);
							
							
							if(z->Act[act].Stage[stage].WaterEnable && y + (a - SpriteSizeYT) + p->Camera_Y_pos >= (water_current_height >> 8)){
								val_pixel = SpriteTable[SpriteAddressTable[s] + 4 + (c + d)];
								
								if(val_pixel != MASK_COLOR_16){
									color_rs = val_pixel >> 11;
									color_gs = (val_pixel >> 5) & 0x3F;
									color_bs = val_pixel & 0x1F;
									
									color_i = *(short *)(&l->line[y + (a - SpriteSizeYT)][(x + (b - SpriteSizeXL))<<1]);
									if(color_i == MASK_COLOR_16 && l == LayerH[frame-1]){
										color_i = *(short *)(&LayerL[frame-1]->line[y + (a - SpriteSizeYT)][(x + (b - SpriteSizeXL))<<1]);
									}
									if(color_i == MASK_COLOR_16 && l == LayerL[frame-1]){
										color_i = *(short *)(&LayerB[frame-1]->line[y + (a - SpriteSizeYT)][(x + (b - SpriteSizeXL))<<1]);
									}
									
									color_rb = color_i >> 11;
									color_gb = (color_i >> 5) & 0x3F;
									color_bb = color_i & 0x1F;
									
									color_rs = (short)(color_rs + (color_rb - color_rs) * (float)(tr * 0.015625));
									color_gs = (short)(color_gs + (color_gb - color_gs) * (float)(tr * 0.015625));
									color_bs = (short)(color_bs + (color_bb - color_bs) * (float)(tr * 0.015625));
									val_pixel = bestfit_color(_current_palette, color_rs, color_gs, color_bs);//makecol(color_rs << 2, color_gs << 2, color_bs << 2);
									
									*(short *)&l->line[(y + (a - SpriteSizeYT))>>(screen_scale_y >> screen_double_y)][((x + (b - SpriteSizeXL))>>(screen_scale_x >> screen_double_x))<<1] = 0; //val_pixel;
								}
							}
							else{
								val_pixel = SpriteTable[SpriteAddressTable[s] + 4 + (c + d)];
								
								if(val_pixel != MASK_COLOR_16){
									color_rs = val_pixel >> 11;
									color_gs = (val_pixel >> 5) & 0x3F;
									color_bs = val_pixel & 0x1F;
									
									color_i = *(short *)(&l->line[(y + (a - SpriteSizeYT))>>(screen_scale_y >> screen_double_y)][((x + (b - SpriteSizeXL))>>(screen_scale_x >> screen_double_x))<<1]);
									if(color_i == MASK_COLOR_16 && l == LayerH[frame-1]){
										color_i = *(short *)(&LayerL[frame-1]->line[(y + (a - SpriteSizeYT))>>(screen_scale_y >> screen_double_y)][((x + (b - SpriteSizeXL))>>(screen_scale_x >> screen_double_x))<<1]);
									}
									if(color_i == MASK_COLOR_16 && l == LayerL[frame-1]){
										color_i = *(short *)(&LayerB[frame-1]->line[(y + (a - SpriteSizeYT))>>(screen_scale_y >> screen_double_y)][((x + (b - SpriteSizeXL))>>(screen_scale_x >> screen_double_x))<<1]);
									}
									
									color_rb = color_i >> 11;
									color_gb = (color_i >> 5) & 0x3F;
									color_bb = color_i & 0x1F;
									
									color_rs = (short)(color_rs + (color_rb - color_rs) * (float)(tr * 0.015625));
									color_gs = (short)(color_gs + (color_gb - color_gs) * (float)(tr * 0.015625));
									color_bs = (short)(color_bs + (color_bb - color_bs) * (float)(tr * 0.015625));
									val_pixel = (color_rs << 11) | (color_gs << 5) | color_bs;
									
									*(short *)&l->line[(y + (a - SpriteSizeYT))>>(screen_scale_y >> screen_double_y)][((x + (b - SpriteSizeXL))>>(screen_scale_x >> screen_double_x))<<1] = val_pixel;
								}
							}
						//}
					}
				}
			}
		}
	}
	else{
		for(a = SpriteSizeYT; a <= SpriteSizeYB; a += (1<<(screen_scale_y >> screen_double_y))){
			for(b = SpriteSizeXL; b <= SpriteSizeXR; b += (1<<(screen_scale_x >> screen_double_x))){
				if(y + (a - SpriteSizeYT) >= 0 && y + (a - SpriteSizeYT) < screen_buffer_y){
					if(x + (b - SpriteSizeXL) >= (16>>screen_scale_x) && x + (b - SpriteSizeXL) < screen_buffer_x-16){
					//	if(SpriteTable[SpriteAddressTable[s] + 4 + (a*SpriteSheetSizeX) + b] != 0xFF){
							if(flip == 0)
								c = a * SpriteSheetSizeX;
							else
								c = (SpriteSizeYB + (SpriteSizeYT - a)) * SpriteSheetSizeX;							
							
							
							if(mirror == 0)
								d = b;
							else
								d = SpriteSizeXR + (SpriteSizeXL - b);
							
							
							if(z->Act[act].Stage[stage].WaterEnable && y + (a - SpriteSizeYT) + p->Camera_Y_pos >= (water_current_height >> 8)){
								val_pixel = SpriteTable[SpriteAddressTable[s] + 4 + (c + d)];
								
								if(val_pixel != MASK_COLOR_16)
									*(short *)&l->line[(y + (a - SpriteSizeYT))>>(screen_scale_y >> screen_double_y)][((x + (b - SpriteSizeXL))>>(screen_scale_x >> screen_double_x)<<1)-(32 >> (screen_scale_x >> screen_double_x))+32] = 0; //val_pixel;
							}
							else{
								val_pixel = SpriteTable[SpriteAddressTable[s] + 4 + (c + d)];
								
								if(val_pixel != MASK_COLOR_16)
									*(short *)&l->line[(y + (a - SpriteSizeYT))>>(screen_scale_y >> screen_double_y)][((x + (b - SpriteSizeXL))>>(screen_scale_x >> screen_double_x)<<1)-(32 >> (screen_scale_x >> screen_double_x))+32] = val_pixel;
							}
						//}
					}
				}
			}
		}
	}
	//}
}

void ClearSprites(){
	int i;
	
	for(i=0; i < MAX_SPRITE_FILES_ALLOWED; i++)
		SpriteAddressTable[i] = 0;
	
	SpriteTableSize = 0;
	
	if((int)(size_t)SpriteTable != ENOMEM){
		free(SpriteTable);
		SpriteTable = 0;
	}
}

void LoadSprite(unsigned short s, const char *file, ...){
	long pos;
	int a;
	int b;
	int i;
	int n;
	int SheetSize;
	
	unsigned short NumberOfSprites;
	unsigned short NumberOfAnimations;
	signed short SpriteSheetSizeX;
	signed short SpriteSheetSizeY;
	
	signed short SpriteSizeXL;
	signed short SpriteSizeXR;
	signed short SpriteSizeYT;
	signed short SpriteSizeYB;
	
	unsigned char NumberOfFrames;
	unsigned char AnimationSpeed;
	
	FILE *psf;
	PALETTE pal;
	
	#ifdef ALLEGRO_DOS
	allegro_message("Loading sprite file \"%s\"...\n", file);
	#endif
	
	psf = fopen(file, "r+b");
	if(psf == NULL){
		allegro_message("Could not open file \"%s\". Program closing...\n", file);
		return;
	}
	
	fread(&NumberOfSprites, 2, 1, psf);
	fread(&NumberOfAnimations, 2, 1, psf);
	fread(&SpriteSheetSizeX, 2, 1, psf);
	fread(&SpriteSheetSizeY, 2, 1, psf);
	
	SheetSize = SpriteSheetSizeX * SpriteSheetSizeY;
	
	//size = SpriteSizeTable[s-1];
	//SpriteAddressTable[s] = size;
	
	SpriteAddressTable[s] = SpriteTableSize;
	SpriteTableSize += (4 + (SheetSize << 2) + (NumberOfSprites << 2) + (NumberOfAnimations << 8));
	if(SpriteTable) SpriteTable = (short *)realloc(SpriteTable, SpriteTableSize << 1);
	else SpriteTable = (short *)malloc(SpriteTableSize << 1);
	
	pos = ftell(psf);	// save our place so we can go back
	fseek(psf, NumberOfSprites << 3, SEEK_CUR);
	
	for(n=0; n < NumberOfAnimations; n++){
		NumberOfFrames = getc(psf);
		AnimationSpeed = getc(psf);
		
		for(i=0; i < NumberOfFrames; i++)
			fread(&a, 2, 1, psf);
	}
	
	
	
	SpriteTable[SpriteAddressTable[s]] = NumberOfSprites;
	SpriteTable[SpriteAddressTable[s]+1] = NumberOfAnimations;
	SpriteTable[SpriteAddressTable[s]+2] = SpriteSheetSizeX;
	SpriteTable[SpriteAddressTable[s]+3] = SpriteSheetSizeY;
	
	get_palette(pal);
	for(n=0; n < SheetSize; n++){
		a = getc(psf);
		b = getc(psf);
		
		SpriteTable[SpriteAddressTable[s] + n + 4] = ((b << 8) | a);
	}
	
	fseek(psf, pos, SEEK_SET);
	for(n=0; n < NumberOfSprites; n++){
		fread(&SpriteSizeXL, 2, 1, psf);
		fread(&SpriteSizeXR, 2, 1, psf);
		fread(&SpriteSizeYT, 2, 1, psf);
		fread(&SpriteSizeYB, 2, 1, psf);
		
		SpriteTable[SpriteAddressTable[s] + (SheetSize << 2) + 4 + (n << 2)] = SpriteSizeXL;
		SpriteTable[SpriteAddressTable[s] + (SheetSize << 2) + 4 + (n << 2)+1] = SpriteSizeXR;
		SpriteTable[SpriteAddressTable[s] + (SheetSize << 2) + 4 + (n << 2)+2] = SpriteSizeYT;
		SpriteTable[SpriteAddressTable[s] + (SheetSize << 2) + 4 + (n << 2)+3] = SpriteSizeYB;
	}
	
	for(n=0; n < NumberOfAnimations; n++){
		NumberOfFrames = getc(psf);
		AnimationSpeed = getc(psf);
		
		//SpriteTable[SpriteAddressTable[s] + (SheetSize << 1) + 4 + (NumberOfSprites << 2) + (n << 8)] = NumberOfFrames;
		SpriteTable[SpriteAddressTable[s] + (SheetSize << 2) + 4 + (NumberOfSprites << 2) + (n << 8)] = AnimationSpeed;
		
		for(i=0; i < NumberOfFrames; i++){
			fread(&a, 2, 1, psf);
			SpriteTable[SpriteAddressTable[s] + (SheetSize << 2) + 4 + (NumberOfSprites << 2) + (n << 8)+1 + i] = a;
		}
		
		for(i = NumberOfFrames; i < 255; i++)
			SpriteTable[SpriteAddressTable[s] + (SheetSize << 2) + 4 + (NumberOfSprites << 2) + (n << 8)+1 + i] = 0xFF;
	}
	
	fclose(psf);
}
